#ifndef FASTESPNOW_H
#define FASTESPNOW_H
#pragma once
#include <Arduino.h>
#include <FastEspnow_command.h> // 相关结构体存储库
#include <FastEspnow_struct.h>  // 相关结构体存储库
#include <WiFi.h>
#include <esp_now.h>

#define CHANNEL 1              // WiFi 通道 默认1
#define SEND_INTERVAL 15       // espnow 发送间隔
#define FAILURES_TIMES 1       // 通讯失败次数, 向节点发送数据, 如果失败次数超过此次数, 将该节点判定为 挂起状态
#define MAX_SCAN_TIMES 10      // 扫描次数
#define PACKET_INTERVAL 500    // 扫描时间间隔
#define FRAME_HEARTBEAT_1 0xff // 校验帧头1
#define FRAME_HEARTBEAT_2 0xfe // 校验帧头2

class ESPNOW : public CConfig
{
private:
    inline static ESPNOW *p;                                    // 静态类指针 指向创建的对象，用于在静态方法中访问非静态属性
    SemaphoreHandle_t x_espnow_send = NULL;                     // espnow 发送等待时间信号量, 避免多线程调用冲突
    TickType_t timeOut = 20;                                    // espnow 发送等待时间, 超过时间放弃发送
    TimerHandle_t heartbeatHandle;                              // 心跳定时器句柄
    QueueHandle_t q_rec_msg = xQueueCreate(8, sizeof(MESSAGE)); // 接收回调消息队列. 避免在回调里面执行太多操作
    EhandshakeStatus sacnStatus = closeConnect;                 // 握手包状态 默认状态 关闭扫描
    uint8_t handShakeAddr[6];                                   // 握手包地址

    uint8_t myConfigArray[MAX_CONFIG_ARRY]; // 本机配置，握手激活时会发送这部分数据
    uint8_t myConfigArrayLength;            // 本机配置长度

    uint8_t tconfigArray[MAX_CONFIG_ARRY]; // 临时配置，收到握手包时，会把数据放到这个数组
    uint8_t tconfigArrayLength;            // 临时配置长度

    typedef void (*peer_state_Func)(peer_info peer);
    typedef void (*peer_config_Func)(peer_info peer, uint8_t *config, uint8_t configlen);
    typedef void (*scan_start_cb_Func)();
    typedef void (*scan_stop_cb_Func)(bool result);
    typedef void (*recMessage_cb_Func)(uint8_t *mac, uint16_t sn, uint8_t commad, uint8_t len, uint8_t *data);

    peer_config_Func _peerOnline_cb = NULL;   // 节点上线函数指针
    peer_state_Func _peerOffline_cb = NULL;   // 节点离线函数指针
    peer_state_Func _peerSupend_cb = NULL;    // 节点挂起函数指针
    peer_config_Func _peerActive_cb = NULL;   // 节点激活函数指针
    scan_start_cb_Func _scan_start_cb = NULL; // 扫描开始函数指针
    scan_stop_cb_Func _scan_stop_cb = NULL;   // 扫描结束函数指针
    recMessage_cb_Func _recMessage_cb = NULL; // 数据接收函数指针

    ESPNOW() { p = this; }                          // 单例模式-私有化构造函数
    bool checkSum(const uint8_t *buf, uint8_t len); // 校验和函数
    void addPeer(const uint8_t *mac, uint16_t sn);  // 添加节点

    void receive_method(); // 接收处理函数
    void scan_method();    // 扫描处理函数
    static void s_scan_method(void *_this) { static_cast<ESPNOW *>(_this)->scan_method(); }
    static void s_receive_method(void *_this) { static_cast<ESPNOW *>(_this)->receive_method(); }

    static void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int dataLen); // 静态方法，用于注册 espnow 接收数据回调
    static void onDataSent(const uint8_t *macAddr, esp_now_send_status_t status);      // 静态方法，用于注册 espnow 发送数据回调

public:
    static ESPNOW &getInstance() // 单例实例的访问器
    {
        static ESPNOW instance;
        return instance;
    }
    ESPNOW &operator=(const ESPNOW &) = delete; // 屏蔽赋值构造
    ESPNOW(const ESPNOW &) = delete;            // 屏蔽拷贝构造

public:
    /**
     * @brief 承诺数据
     *
     * 1. 对于部分一定需要送达但是对时间不敏感的数据, 可以通过 CMD_PROMISE 指令来发送承诺指令
     * 2. 设备接收到 CMD_PROMISE 后会发送 CMD_PROMISE_ASK 指令进行应答
     * 3. A设备向B设备,发送承诺数据后,如果B设备出于断电或者距离太远导致无法正常接收到,设备 A 会存储该指令内容, 在 B 设备向A设备发送数据的时候, 再尝试进行发送, 确保设备 B 能收到数据.
     * 4. 承诺数据一般用于确保配置信息的发送, 比如 A 是一个无线适配器, B 是设备, 当 A 试图修改 B 的配置时,如果 B 正好处于休眠状态或者信号不佳的位置, 在 B 重新链接后, 能确保一定能接收到配置信息.
     * 5. 每个设备暂时只支持一条承诺信息, 再次发送的承诺数据会覆盖掉内存中旧的信息
     */
    uint8_t promiseCount; // 承诺数据个数
    MESSAGE promise[20];  // 承诺数据数组

    void attach_peerOnline_cb(peer_config_Func newFunction) { _peerOnline_cb = newFunction; }   // 新设备上线回调函数
    void attach_PeerOffline_cb(peer_state_Func newFunction) { _peerOffline_cb = newFunction; }  // 设备离线回调函数
    void attach_peerSupend_cb(peer_state_Func newFunction) { _peerSupend_cb = newFunction; }    // 设备挂起
    void attach_peerActive_cb(peer_config_Func newFunction) { _peerActive_cb = newFunction; }   // 设备激活
    void attach_strat_scan_cb(scan_start_cb_Func newFunction) { _scan_start_cb = newFunction; } // 开始扫描函数
    void attach_stop_scan_cb(scan_stop_cb_Func newFunction) { _scan_stop_cb = newFunction; }    // 结束扫描回调函数
    void attach_RecMessage_cb(recMessage_cb_Func newFunction) { _recMessage_cb = newFunction; } // 接收回调函数

    void supend();                                          // 挂起 ESPNOW 向所有节点发送挂起信息
    void active();                                          // 激活 ESPNOW 向所有节点发送激活信息
    void clearAllPeers();                                   // 删除所有节点
    void getActivePeers();                                  // 获取所有激活节点
    void removePeer(int peerIndex, bool notifyPeer = true); // 删除节点,是否通知对方

    void scan();                                                                                // 开启扫描
    void begin(uint16_t sn, void *config = nullptr, uint8_t len = 0, bool isFristTime = false); // 初始化
    void openHeartBeat(uint16_t interval = 1000);                                               // 开启心跳
    void closeHeartBeat();                                                                      // 关闭心跳

    void sendMessage(const uint8_t *address, MESSAGE msg);                         // ESPNOW 发送函数 信息结构体
    void sendMessage(const uint8_t *address, uint8_t _cmd, uint8_t _len = 0, ...); // ESPNOW 发送函数 不定参数

    void checkPromise(const uint8_t *macAddr, uint16_t sn); // 检测该sn是否有未发送的数据
};
extern ESPNOW &Espnow; // 单例模式

#endif